package org.raymond.iworks.gateway.utils;

import cn.hutool.crypto.digest.DigestUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.raymond.iworks.commons.utils.ErrorUtil;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class SignUtil {
    @Setter
    private ObjectMapper objectMapper;

    public boolean checkSign(MultiValueMap dataMap, String sign) {
        if (MapUtils.isEmpty(dataMap)) {
            return false;
        }
        try {
            String signNow = signMultiMap(dataMap);
            if (log.isDebugEnabled()) {
                log.debug("sha256sorted:{}, sign:{}", signNow, sign);
            }
            return signNow.equals(sign);
        } catch (Exception e) {
            log.error("{}", ErrorUtil.getErrorLog(e));
        }
        return false;
    }

    public String signMap(Map<String, Object> dataMap){
        String result = "";
        if (MapUtils.isEmpty(dataMap)) {
            return result;
        }
        try {
            MultiValueMap multiValueMap = new LinkedMultiValueMap(
                    dataMap.entrySet().stream().collect(Collectors.toMap(
                            Map.Entry::getKey, e->Collections.singletonList(e.getValue()))));
            result = signMultiMap(multiValueMap);
        } catch (Exception e) {
            log.error("{}", ErrorUtil.getErrorLog(e));
        }
        return result;
    }

    public String signMultiMap(MultiValueMap dataMap){
        String result = "";
        if (MapUtils.isEmpty(dataMap)) {
            return result;
        }
        try {
            Map<String, String[]> sorted = sort(dataMap, true);
            String sortedStr = objectMapper.writeValueAsString(sorted);
            result = DigestUtil.sha256Hex(sortedStr);
        } catch (Exception e) {
            log.error("{}", ErrorUtil.getErrorLog(e));
        }
        return result;
    }

    private Map<String, String[]> sort(Map<String, String[]> dataMap, boolean asc) {
        Map<String, String[]> sorted = null;
        if (asc) {
            sorted = dataMap.entrySet().stream().sorted(Map.Entry.comparingByKey((k1, k2) -> {
                if (k1.length() == k2.length()) {
                    return k1.compareTo(k2);
                } else {
                    return k1.length() - k2.length();
                }
            })).collect(Collectors.toMap(
                    Map.Entry::getKey,
                    Map.Entry::getValue,
                    (oldVal, newVal) -> oldVal,
                    LinkedHashMap::new
            ));
        } else {
            sorted = dataMap.entrySet().stream().sorted(Map.Entry.comparingByKey((k1, k2) -> {
                if (k1.length() == k2.length()) {
                    return k2.compareTo(k1);
                } else {
                    return k2.length() - k1.length();
                }
            })).collect(Collectors.toMap(
                    Map.Entry::getKey,
                    Map.Entry::getValue,
                    (oldVal, newVal) -> oldVal,
                    LinkedHashMap::new
            ));
        }
        return sorted;
    }
}
